package com.mypaper.dag;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;

/*
 * outdegree={1,2,3,4,……,2*width};
 */

public class DAGGenerator
{
	public int PROCNUM;//处理器数量；缺省权限为包权限;值为（8,16,32,64……）
	public int TASKNUM;//任务数；（36,64,100,144,196……）
	public float[][] comp;
	public float[][] comm;
	private int comp_base = 20;
	private float PARAFAC;//并行度因子alpha（0.5,1.0,2.0）
	private float BEITA = 0.5f;//处理器异构因子（0.1,0.25,0.5，0.75,1,1.25,1.5）这里设置不能大于1.5；
	private float CCR;//通信计算比（0.2,0.5,1,2,5）
	private int depth;//DAG深度
	private int[] perLevel;//每层任务数
	private DAGTaskInfo[] task;//包含任务入度和出度
	private static int FileWriterCount;
	private int width;
	public DAGGenerator()
	{
		CCR = (float)2;
		TASKNUM = 16;
		PROCNUM = 4;
		PARAFAC = (float) 1;
		comp = new float[TASKNUM][PROCNUM];
		comm = new float[TASKNUM][TASKNUM];
		float radomDepthParam = (float) (Math.random()*0.6) + 0.7f;//随机深度因子0.7~1.3；
		depth = (int)(Math.sqrt(TASKNUM)*radomDepthParam/PARAFAC);//深度
		width = (int)(Math.sqrt(TASKNUM)*PARAFAC/radomDepthParam);//平均宽度
		while(depth > TASKNUM)
		{
			depth--;
		}
		while(depth*width < TASKNUM)
		{
			width++;
		}
		perLevel = new int[depth];
		task = new DAGTaskInfo[TASKNUM];
		for(int i = 0; i < TASKNUM; i++)
		{
			task[i] = new DAGTaskInfo();
		}
		
		findTaskNumPerLevel();
		setTask();
		setProc();
		//updateDAG();
		//DAGFileWriter();
	}
	
	public DAGGenerator(int PROCNUM, int TASKNUM, float PARAFAC, float CCR)
	{
		this.PROCNUM = PROCNUM;
		this.TASKNUM = TASKNUM;
		this.PARAFAC = PARAFAC;
		this.CCR = CCR;
		comp = new float[TASKNUM][PROCNUM];
		comm = new float[TASKNUM][TASKNUM];
		float radomDepthParam = (float) (Math.random()*0.6) + 0.7f;//随机深度因子0.7~1.3；
		depth = (int)((Math.sqrt(TASKNUM)/PARAFAC) * radomDepthParam);
		width = (int)(Math.sqrt(TASKNUM)*PARAFAC/radomDepthParam);//平均宽度
		while(depth > TASKNUM)
		{
			depth--;
		}
		while(depth*width < TASKNUM)
		{
			width++;
		}
		perLevel = new int[depth];
		task = new DAGTaskInfo[TASKNUM];
		for(int i = 0; i < TASKNUM; i++)
		{
			task[i] = new DAGTaskInfo();
		}
		
		findTaskNumPerLevel();
		setTask();
		setProc();
//		System.out.println("update dag");
//		updateDAG();
//		DAGFileWriter();
	}
	
	
	public void updateDAG()
	{
		//用来删除一些多余的边；
		float[] inDegree = new float[TASKNUM];
		float[] outDegree = new float[TASKNUM];
		int inDegree1count = 0;
		for(int i=0; i<TASKNUM; i++)
		{
			for(int j=0; j<TASKNUM; j++)
			{
				if(comm[i][j]!=0)
				{
					outDegree[i]++;
					inDegree[j]++;
				}
			}
			
		}
		for(int i = 0; i < TASKNUM; i++)
		{
			if(inDegree[i] == 1)
				inDegree1count++;
			//System.out.println("inDegree["+ i +"]="+inDegree[i]+" outDegree["+i+"]="+outDegree[i]);
		}
		//System.out.println("inDegree1count="+inDegree1count);
		int inCount = TASKNUM/4+1;
		System.out.println("inDegree1count--------="+inDegree1count);
		while(inDegree1count < inCount)
		{
			int taskNo =  (int) Math.floor(Math.random()*TASKNUM);
			int index = (int) (Math.random()*TASKNUM/2);
			if(inDegree[taskNo] > 1)
			{
				for(int i = index; i < TASKNUM; i++)
				{
					
					if(comm[i][taskNo] != 0 && inDegree[taskNo] > 1 && outDegree[i] > 1)
					{
						comm[i][taskNo] = 0;
						inDegree[taskNo]--;
						outDegree[i]--;
						if(inDegree[taskNo]==1)
						{
							inDegree1count++;
							break;
						}
					}
				}
				
			}
		}
		System.out.println("inDegree1count:"+inDegree1count+"TASKNUM/4+1="+(TASKNUM/4+1));
		for(int i = 0; i < TASKNUM; i++)
		{
			System.out.println("inDegree["+ i +"]="+inDegree[i]+" outDegree["+i+"]="+outDegree[i]);
		}
//		System.out.println("inDegree1count="+inDegree1count+"----dag gen over");
	}
	
	public void findTaskNumPerLevel()
	{
		int count = 2;
		perLevel[0] = 1;
		perLevel[depth - 1] = 1;
		for(int i = 1; i < depth - 1; i++)
		{
			perLevel[i] = getRand();
			count += perLevel[i];
		}
		if(count > TASKNUM)
		{
			int countBack = count - TASKNUM;
			int temp = 1;
			while(countBack > 0)
			{
				temp = (int)(Math.random()*(depth - 1)) + 1;
				//System.out.println("temp:"+temp);
				if(temp >= depth - 1)temp--;
				perLevel[temp]--;
				countBack--;
				if(perLevel[temp] == 0)
				{
					perLevel[temp]++;
					countBack++;
				}
			}
		}else if(count < TASKNUM)
		{
			int countBack = TASKNUM - count;
			int temp = 1;
			while(countBack > 0)
			{
				temp = (int)(Math.random()*(depth - 1)) + 1;
				if(temp >= depth - 1)temp--;
				perLevel[temp]++;
				countBack--;
				if(perLevel[temp] > 2*width)
				{
					countBack++;
					perLevel[temp]--;
				}
			}
			
		}
		
	}
	public int getRand()
	{
		int alpha;
		alpha = (int)(Math.random()*2*width);
		if(alpha == 0)alpha = 1;
		return alpha;
	}
	
	public void setTask()
	{
		int[] index = new int[depth + 1];
		index[0] = 0;
		float comm_base = comp_base * CCR;
		for(int i = 1; i <= depth; i++)
		{
			index[i] = index[i-1] + perLevel[i-1];//每层任务的起始下标索引
		}
		
		for(int i = 0; i < depth - 1; i++)//遍历各层,取不到最后一层；
		{
//			if(i == depth-1 && perLevel[i] == 1)
//			{
//				//如果是退出节点，什么都不做
//			} 
//			else 
				if(perLevel[i] == 1 && i != depth-1)
				{
					task[i].out = perLevel[i+1];//一层若只有一个节点且不是最后一层，下一层节点都是其孩子
					for(int k = index[i+1]; k < index[i+2]; k++)
					{
						comm[i][k] = (int)((Math.random()+0.5)*comm_base/2) + comm_base/2;//节点到下层孩子的通信量
						task[k].in++;
					}
				}
				else
				{
					int randomOut = (int)index[i+1]/index[i];
					for(int j = index[i]; j < index[i+1]; j++)
					{
						task[j].out = (int)(Math.random()*2*randomOut+1);
						while(task[j].out > perLevel[i+1])task[j].out--;
						for(int k = 0; k < task[j].out; k++)
						{
							int rand = (int)(Math.random()*perLevel[i+1] + index[i+1]);//选取孩子节点的下标
							if(rand >= TASKNUM)//防止极端情况下,上行随机数为1，倒数第二层任务的孩纸取到exit任务后的值
							{
								rand--;//因而减一；
							}
							comm[j][rand] = (int)(Math.random()*comm_base/2) + comm_base/2;//产生通信代价
							task[rand].in++;
						}
					}
				}
		}
		for(int i = 1; i < TASKNUM; i++)
		{
			if(task[i].in == 0)
			{
				int rand = (int)(Math.random()*i);//给没有父节点的节点选择父节点
				if(task[rand].out >= 2*width)
				{
					i--;
					continue;
				}
				comm[rand][i] = (int)(Math.random() * comm_base/2) + comm_base/4;
				task[i].in++;
				task[rand].out++;
			}
		}
		
	}
	
	public void setProc()
	{
		if(BEITA < -1.5 || BEITA > 1.5)
		{
			
			try
			{
				throw new Exception();
			} catch (Exception e)
			{
				System.out.println("处理器异构因子设置有误！");
				e.printStackTrace();
				System.exit(0);
			}
		}
		for(int i = 0; i < TASKNUM; i++)
		{
			for(int j = 0; j < PROCNUM; j++)
			{
				comp[i][j] = (int)(((Math.random()-0.5)*BEITA + 1)*comp_base);
			}
		}
	}
	
	public void DAGFileWriter()
	{
		FileWriterCount++;
		String fileName = Integer.toString(FileWriterCount);
		while(fileName.length() != 4)
		{
			fileName = "0" + fileName;
		}
		fileName = fileName + ".txt";
		String data = new SimpleDateFormat("MM-dd-").format(Calendar.getInstance().getTime());
		String folderPath = "D:\\MyEclipse Professional\\workspace\\HTR\\FileWriterDAG\\";
		String pathAndCommFileName = folderPath + data + "Comm"+ fileName;
		String pathAndCompFileName = folderPath + data + "Comp" + fileName;
		File commFile = new File(pathAndCommFileName);
		File compFile = new File(pathAndCompFileName);
		try
		{
			FileWriter fwComm = new FileWriter(commFile);
			for(int i = 0; i < TASKNUM; i++)
			{
				for(int j = 0; j < TASKNUM; j++)
				{
					if(comm[i][j] > 10)
					{
						fwComm.write(comm[i][j] + "\t");
					}
					else
					{
						fwComm.write(comm[i][j] + "\t\t");
					}
				}
				fwComm.write("\n");
			}
			fwComm.close();
			FileWriter fw = new FileWriter(compFile);
			
			for(int i = 0; i < TASKNUM; i++)
			{
				for(int j = 0; j < PROCNUM; j++)
				{
					fw.write(comp[i][j] +"\t\t");
				}
				fw.write("\n");
			}
			fw.close();
			System.out.println("The folder path of DAG-cost is: " + folderPath);
			
		} catch (IOException e)
		{
			e.printStackTrace();
		}
		
	}
	
	public void DAGFileReader()
	{
		String folderPath = "D:\\MyEclipse Professional\\workspace\\HTR\\FileWriterDAG\\";
		String commFileName = "03-20-Comm0001.txt";
		String compFileName = "03-20-Comp0001.txt";
		try
		{
			FileReader fr = new FileReader(folderPath + commFileName);
			BufferedReader br = new BufferedReader(fr);
			String line = null;
			String[] sp = null;
			int lineCount = 0;
			try
			{
				while((line = br.readLine()) != null)
				{
					sp = line.replace("\t\t", "\t").split("\t");
					for(int j = 0; j < sp.length; j++)
					{
						comm[lineCount][j] = Float.parseFloat(sp[j]);
					}
					lineCount++;
				}
				TASKNUM = comm[0].length;
				br.close();
				fr.close();
				line = null;
				sp = null;
				lineCount = 0;
				fr = new FileReader(folderPath + compFileName);
				br = new BufferedReader(fr);
				while((line = br.readLine()) != null)
				{
					sp = line.replace("\t\t", "\t").split("\t");
					for(int j = 0; j < sp.length; j++)
					{
						comp[lineCount][j] = Float.parseFloat(sp[j]);
					}
					lineCount++;
				}
				PROCNUM = comp[0].length;
				
			} catch (IOException e)
			{
				e.printStackTrace();
			}
			
//			System.out.println("---------以下为读取到的通信代价-------");
//			for(int i = 0; i < comm.length; i++)
//			{
//				for(int j = 0; j < comm[0].length; j++)
//				{
//					System.out.print(comm[i][j] + " ");
//				}
//				System.out.println();
//			}
//			System.out.println("---------任务数量：" + TASKNUM);
//			System.out.println("---------以下为读取到的计算代价-------");
//			for(int i = 0; i < comp.length; i++)
//			{
//				for(int j = 0; j < comp[0].length; j++)
//				{
//					System.out.print(comp[i][j] + " ");
//				}
//				System.out.println();
//			}
//			System.out.println("---------处理器数量:" + PROCNUM);
			
		} catch (FileNotFoundException e)
		{
			e.printStackTrace();
		}
		
		
	}
	
	public int getComp_base()
	{
		return comp_base;
	}
	public void setComp_base(int comp_base)
	{
		this.comp_base = comp_base;
	}
	public int getPROCNUM()
	{
		return PROCNUM;
	}
	public void setPROCNUM(int pROCNUM)
	{
		PROCNUM = pROCNUM;
	}
	public int getTASKNUM()
	{
		return TASKNUM;
	}
	public void setTASKNUM(int tASKNUM)
	{
		TASKNUM = tASKNUM;
	}
	public float getPARAFAC()
	{
		return PARAFAC;
	}
	public void setPARAFAC(float pARAFAC)
	{
		PARAFAC = pARAFAC;
	}
	public float getCCR()
	{
		return CCR;
	}
	public void setCCR(float cCR)
	{
		CCR = cCR;
	}
	public int getDepth()
	{
		return depth;
	}
	public void setDepth(int depth)
	{
		this.depth = depth;
	}
	public int[] getPerLevel()
	{
		return perLevel;
	}
	public void setPerLevel(int[] perLevel)
	{
		this.perLevel = perLevel;
	}
	public DAGTaskInfo[] getTask()
	{
		return task;
	}
	public void setTask(DAGTaskInfo[] task)
	{
		this.task = task;
	}

}
